#!/usr/bin/perl
# by Chris Coletta Wed Mar 17, 2010
# 
# The purpose of this script is to be a harness for the compiled wndchrm binary
# that executes many different types of WND-CHARM experiments and automates the 
# various tasks associated with setting up the experiment.
# This script will parse a text file which is in a certain format (see comments at end of
# this file for a description of the format) that describes and fully defines all aspects of
# the experimental set up. The text file can be saved as a record of exactly what was done,
# encouraging consistency and repeatability from experiment to experiment.
 
# You can also test out configurations before actually carrying out experiments by
# specifying -dryrun on the command line when running this script

# Use -verbose to view debug output

use strict;
use warnings;
use File::Copy;

sub RunExperiment;

if( $#ARGV < 0 ) {
  print "Insufficient arguments.\n";
  print "Usage: experiment [-verbose] [-dryrun] <experiment config file>\n";
  print "Try again!";
  die;
}

my $config_file = undef; # = "/home/colettace/data/monkeys/experimental_sets/four_way_twenty_slide/four_way_twenty_slide.experiment";
my $dryrun = 0;
my $verbose = 0;
for( my $z = 0; $z <= $#ARGV; $z++ ) {
  # print "Arg $z: $ARGV[$z] \n";
  if( -e $ARGV[$z] ) {
    $config_file = $ARGV[$z];
  }
  elsif( $ARGV[$z] eq '-dryrun' ) {
    $dryrun = 1;
    print "Executing dry run only...\n" ;
  }
  elsif( $ARGV[$z] eq '-verbose' ) {
    $verbose = 1;
    print "Printing debug output\n";
  }
  else {
    print "Don't recognize argument \"$ARGV[$z]\"\n";
    print "Usage: experiment [-verbose] [-dryrun] <experiment config file>\n";
    print "Try again!";
    die;
  }
}
if( !defined $config_file ) {
  print "You didn't specify an experiment file..\n";
  print "Usage: experiment [-verbose] [-dryrun] <experiment config file>\n";
  print "Try again!";
  die;
}
  
open( CONFIG_FILE, $config_file ) or die "Could not open experiment file: $!";

my $DEBUG1 = 0;

my $experiment_name = undef;
my $path_to_wndchrm = undef;
my $source_dir = undef;
my $target_dir = undef;
my $tile_scheme = undef;
my $large_feature_set = undef;
my $training_images = undef;
my $test_images = undef;
my $test_repetitions = undef;
my $feature_usage_fraction = undef;
# the following three are used if the user wants to try a range of
# feature usage fractions to optimize classification accuracy
my $start_fraction = undef;
my $finish_fraction = undef;
my $fraction_increment_interval = undef;
my $html_file = undef;
my $write_fisher_weights = undef;
my $training_args = undef;
my $test_args = undef;
my $silence_wndchrm = undef;
my $experiment_repetitions = 1;
my $pairwise_flag = 0;
my $path_to_phylip = undef;
my $weighted_nearest_neighbor = undef;
my $self_set = undef;
my $no_tiffs = undef;
my $specify_train_test_split = undef;
my $phylip_command = undef; # use a separate string for for storing phylip part of wndchrm command
                            # line because we need to keep track of where it is to copy dend files.
my %group_info;
my %class_config_info;

if( $DEBUG1 or $verbose or $dryrun ) {
  print "Reading in config file $config_file\n";
}
while( <CONFIG_FILE> )
{
  if( $DEBUG1 or $verbose or $dryrun ) {
    print $_;
  }
  if( $_ =~ /^#/ )
  {
    if( $DEBUG1 or $verbose or $dryrun ) {
      print "\tComment line skipped.\n";
    }
    next;
  }
  elsif( $_ =~ /^experiment\s+(\S*)/ ) {
    $experiment_name = $1;
    print "\tExperiment name: $experiment_name\n" if( $DEBUG1 or $verbose or $dryrun);
  }
  elsif( $_ =~ /^path_to_wndchrm\s+(\S*)/ ) {
    $path_to_wndchrm = $1;
    print "\tPath to wndchrm: $path_to_wndchrm\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^source_dir\s+(\S*)/ ) {
    $source_dir = $1;
    print "\tSource dir: $source_dir\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^target_dir\s+(\S*)/ ) {
    $target_dir = $1;
    print "\tTarget dir: $target_dir\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^class\s+(\S+)\s+(\S+)\s+(\S+)\s+group\s+(\S+)/ )
  {
    # Sturcture of hash table %class_config_info:
    #   - keys %class_config_info = the name of the class in the experiment, into which
    #       images from multiple directories will go
    #   - $class_config_info{ "some key" } = reference to an array of hashes
    #   - @{ $class_config_info{ "some key" } } = an array of hashes containing the configuration
    #       info for each directory's images.
    $pairwise_flag = 1; # let the script know that we'll have to do pairwise experiment
    if( $DEBUG1 or $verbose or $dryrun ) {
      print "\tclass $2 from dir $1: group $4, num images $3\n";
    }
    push @{ $class_config_info{$2} }, { dir_name => $1, img_quant => $3, group => $4 };
    push @{ $group_info{$4} }, $2;
  }
  elsif( $_ =~ /^class\s+(\S+)\s+(\S+)\s+(\S+)/ )
  {
     if( $DEBUG1 or $verbose or $dryrun ) {
       print "\t$3 images (and associated files) from directory $1 will go into class $2\n";
     }
     push @{ $class_config_info{$2} }, { dir_name => $1, img_quant => $3 };
  }
  elsif( $_ =~ /^training_image\s+(\S+)\s+dir\s+(\S+)\s+class\s+(\S+)/ )
  {
    $specify_train_test_split = 1; # let the script know that the user is specifying train test splits
    if( $DEBUG1 or $verbose or $dryrun ) {
      print "\ttraining image $1 from dir $2 will go into class $3\n";
    }
    push @{ $class_config_info{$3}->{training_set} }, { name => $1, dir => $2 };
  }
  elsif( $_ =~ /^test_image\s+(\S+)\s+dir\s+(\S+)\s+class\s+(\S+)/ )
  {
    $specify_train_test_split = 1; # let the script know that the user is specifying train test splits
    if( $DEBUG1 or $verbose or $dryrun ) {
      print "\ttest image $1 from dir $2 will go into class $3\n";
    }
    push @{ $class_config_info{$3}->{test_set} }, { name => $1, dir => $2 };
  }
  elsif( $_ =~ /^tile_scheme\s+(\d)/ ) {
    $tile_scheme = $1;
    print "\tTile scheme: $tile_scheme X $tile_scheme\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^large_feature_set/ ) {
    $large_feature_set = 1;
    print "\tLarge feature set will be used if sigs need to be trained.\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^training_images\s+(\d*)/ ) {
    $training_images = $1;
    print "\tNumber of training images: $training_images\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^test_images\s+(\d*)/ ) {
    $test_images = $1;
    print "\tNumber of test_images: $test_images\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^test_repetitions\s+(\d*)/ ) {
    $test_repetitions = $1;
    print "\tNumber of test repetitions: $test_repetitions\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^feature_usage_fraction\s+(\d*\.\d+)/ ) {
    $feature_usage_fraction = $1;
    print "\tFraction of features to be used: $feature_usage_fraction\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^vary_feature_fraction/ ) {
    if( $_ =~ /^vary_feature_fraction\s+start\s+(\d?\.\d+)\s+finish\s+(\d?\.\d+)\s+increment\s+([+-]?\d?\.\d+)/ ) {
      die "\n\nCONFIGURATION FILE ERROR:\nFeature fraction paramaters must be > 0 and < 1.\n" 
        if( ! ( $1 >= 0 && $1 <= 1 && $2 >= 0 && $2 <= 1 && $3 >= 0 && $3 <= 1 ) );
      if( $2 > $1 ){
        $start_fraction = $1;
        $finish_fraction = $2;
      } else {
        $start_fraction = $2;
        $finish_fraction = $1;
      }
      $fraction_increment_interval = $3; 
      if( $DEBUG1 or $verbose or $dryrun ) {
        print "\tFeature usage fraction will be varied from $start_fraction to $finish_fraction by interval $fraction_increment_interval\n";
      }
    } else {
      die "\n\n\nCONFIGURATION FILE ERROR: vary_feature fraction line incorrectly formatted. Example format is:\n\nvary_feature_fraction  start  0.0 finish  1.0 increment 0.05\n";
    }
  }
  elsif( $_ =~ /^html_file/ ) {
    $html_file = 1;
    print "\tHTML output file will be generated\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^write_fisher_weights/ ) {
    $write_fisher_weights = 1;
    print "\tFisher weights file will be generated\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^experiment_repetitions\s+(\d+)/ ) {
    $experiment_repetitions = $1;
    print "\tExperiment will be repeated $experiment_repetitions times.\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^additional_test_args\s+(.*)/ ) {
    $test_args = $1;
    print "\tThe following additional arguments will be applied at test time: $test_args\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^silence_wndchrm/ ) {
    $silence_wndchrm = 1;
    print "\tWNDCHRM will be run in silent mode.\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^path_to_phylip\s+(\S*)/ ) {
    $path_to_phylip = $1;
    print "\tPath to phylip: $path_to_phylip\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^weighted_nearest_neighbor/ ) {
    $weighted_nearest_neighbor = 1;
    print "\tUsing WNN (Weighted Nearest Neighbor)\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^self_set/ ) {
    $self_set = 1;
    print "\tUsing the entire training set as the test set.\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  elsif( $_ =~ /^no_tiffs/ ) {
    $no_tiffs = 1;
    print "\tUsing sig files as the samples and disregarding tiffs.\n" if( $DEBUG1 or $verbose or $dryrun );
  }
  else {
    if( $DEBUG1 or $verbose or $dryrun ) {
      print "\tCan't read anything from this line... Skipping.\n"
    }
  }
}
close CONFIG_FILE;

if( !defined $source_dir ) {
  die "No source dir defined in the experiment file.";
}
if( !defined $target_dir ) {
  die "No target dir defined in the experiment file.";
  # print "No target dir specified. Using source directory as target dir. No experimental directory will be made.\n";
}
if( !defined $path_to_wndchrm ) {
  die "Cannot continue, no path to wndchrm defined in experiment file.";
}
if( defined $tile_scheme ) {
  $tile_scheme = "-t" . $tile_scheme;
} else {
  $tile_scheme = "";
}
if( defined $large_feature_set ) {
  $large_feature_set = "-l";
} else {
  $large_feature_set = "";
}
if( defined $training_images ) {
  $training_images = "-i" . $training_images;
} else {
  $training_images = "";
}
if( defined $test_images ) {
  $test_images = "-j" . $test_images;
} else {
  $test_images = "";
}
if( defined $feature_usage_fraction ) {
  $feature_usage_fraction  = "-f" . $feature_usage_fraction ;
} else {
  $feature_usage_fraction  = "";
}
if( defined $test_repetitions ) {
  $test_repetitions = "-n" . $test_repetitions;
} else {
  $test_repetitions = "";
}
# The html file name and fisher weights file name are now taken care of in the loop,
#  since the target directory and the experiment name change with subsequent experiment 
#  repetitions.
# if( defined $html_file ) {
#   $html_file = "$target_dir/$experiment_name.html";
# } else {
#   $html_file = "";
# }
# if( defined $write_fisher_weights ) {
#   $write_fisher_weights = "-vw$target_dir/$experiment_name.fisher_weights";
# } else {
#   $write_fisher_weights = "";
# }
if( ! defined $test_args ) {
  $test_args = "";
}
if( defined $path_to_phylip ) {
  $phylip_command  = "-p" . $path_to_phylip ;
} else {
  $phylip_command  = "";
}
if( defined $silence_wndchrm ) {
  $silence_wndchrm  = " > /dev/null 2>&1" ;
} else {
  $silence_wndchrm  = "";
}
if( defined $weighted_nearest_neighbor ) {
  $weighted_nearest_neighbor  = " -w" ;
} else {
  $weighted_nearest_neighbor  = "";
}

##################################################################################
# Run the appropriate number of experiments, given by the $experiment_repetitions parameter
##################################################################################

my $DEBUG3 = 0;

# If $experiment_repetitions = 1, then no sub directory is required

if( $DEBUG3 or $verbose or $dryrun ) {
	print "Number of experiments to be run: $experiment_repetitions \n";
}

# group_row and group_col are globals that are used by the RunExperiment
# function to direct pairwise experiment traffic
my $group_row;
my $group_col;
my $original_experiment_name = $experiment_name;
my $original_target_dir = $target_dir;
my $reverse_experiment_dir;
if( $experiment_repetitions == 1 ) {
  if( $pairwise_flag ){
    # Make a top level directory to contain the individual experiments
    $original_target_dir .= "/" . $experiment_name;
    if( $DEBUG3 or $verbose or $dryrun ) {
      print "Making top-level organizational directory $target_dir\n";
    }
    if( !$dryrun ) { mkdir "$original_target_dir" or print $! };
    foreach $group_row (sort keys %group_info ) {
      foreach $group_col (sort keys %group_info ) {
        next if $group_row eq $group_col;
        # Check to see that pairwise experiment "X vs Y" wasn't already done by
        # looking to see if a "Y vs X" directory was created
        $target_dir = $original_target_dir;
        $reverse_experiment_dir = $target_dir . "/" . $group_col . "vs" . $group_row . "_" . $group_info{$group_col}[0] . "_VS_" . $group_info{$group_row}[0];
        if( $DEBUG3 or $verbose or $dryrun ) {
            print "\tChecking to see if experiment $group_row vs $group_col aready done.\n";
          print "\tDoes $reverse_experiment_dir exist?\n";
        }
        next if -e $reverse_experiment_dir;
        # The experiment name changes every time
        $experiment_name = $group_row . "vs" . $group_col . "_" . $group_info{ $group_row }[0] . "_VS_" . $group_info{ $group_col }[0];
        # Reset the target_dir every time you run, because $target_dir gets
        # modified in the RunExperiment subroutine
        if( $DEBUG3 or $verbose or $dryrun ) {
          print "\tRunning pairwise experiment $group_row vs $group_col: $experiment_name\n";
        }
        &RunExperiment( { group_1 => $group_row, group_2 => $group_col } );
      }
    }
  } else {
    if( defined $specify_train_test_split ) {
      &RunExperimentTrainTestSplitsSpecified;
    }
    else {
      &RunExperiment;
    }
  }
}
elsif( $experiment_repetitions > 1 ) {
  # Make a top level directory to contain the individual experiments
  $original_target_dir .= "/" . $experiment_name;
  if( $DEBUG3 or $verbose or $dryrun ) {
    print "Making top-level organizational directory $original_target_dir\n";
  }
  if( !$dryrun ) { mkdir "$original_target_dir" or print $! };
  for( my $ii = 1; $ii <= $experiment_repetitions; $ii++ ) {
    # The experiment name changes every time
    $experiment_name = $original_experiment_name . "_" . $ii;
    # Reset the target_dir every time you run, because $target_dir gets
    # modified in the RunExperiment subroutine
    $target_dir = $original_target_dir;
    if( $DEBUG3 or $verbose or $dryrun ) {
      print "\tRunning experiment $ii: $experiment_name\n";
    }
    &RunExperiment;
  } 
}

##################################################################################
# Here's where all the magic happens, the linking the training and the testing
##################################################################################
my $DEBUG2 = 0;

sub RunExperiment {
  if( $DEBUG2 or $verbose or $dryrun ) {
    print "\n\n*******************************\nBegin experiment $experiment_name:\n";
  }
  my ($args) = @_;
  if( $pairwise_flag ) {
    if( !$args->{group_1} || !$args->{group_2} ) {
      die "Internal Error: Pairwise experiment attempted, but the two groups needed weren't properly defined.\n";
    } else {
      print "\tPairwise analysis between group $args->{group_1} and group $args->{group_2}\n";
    }
  }
  my $class_name;
  my $dir_name;
  my $num_imgs;
  my @full_file_list;
  my @tiff_file_list;
  my @sig_file_list;
  my $file;
  my $file_stem;
  my( $i, $j );

  $target_dir .= "/" . $experiment_name;
  if( $DEBUG2 or $verbose or $dryrun ) {
    print "\tMaking experiment directory $target_dir\n";
  }
  if( -e $target_dir ) {
    print "\t*******$target_dir exists... skipping...\n\n";
    return;
  }
  if( !$dryrun ) { mkdir "$target_dir" or die $! };
  my $fof_path = "$target_dir/${experiment_name}.fof";
  open FOF, '>', $fof_path  or die "Can't open ${experiment_name}.fof for writing the file of files\n";
  ITERATE_OVER_CLASSES: foreach $class_name ( sort keys %class_config_info ) {
    if( $DEBUG2 or $verbose or $dryrun )
    {
      print "\tClass $class_name:\n";
    }
    if( $pairwise_flag )
    {
      # Check the %group_info hash to see if this class $class_name belongs to 
      # one of the groups we're checking
      my $in_this_group = 0;
      my @classes_in_current_groups = ( @{ $group_info{ $args->{group_1} } }, @{ $group_info{ $args->{group_2} } } );
      CHECK_IF_CLASS_IN_GROUP: foreach (@classes_in_current_groups)
      {
        if( $class_name eq $_ )
        {
          $in_this_group = 1;
          last CHECK_IF_CLASS_IN_GROUP;
        }
      }
      if( !$in_this_group )
      {
        if( $DEBUG2 or $verbose or $dryrun )
        {
          print "\t\tSkipping over $class_name because it doesn't belong to groups $args->{group_1} and $args->{group_2}.\n";
        }
        next ITERATE_OVER_CLASSES;
      }
    } # END if $pairwise_flag 
#    if( $DEBUG2 or $verbose or $dryrun )
#    {
#      print "\t\tMaking directory $target_dir/$class_name\n";
#    }
#    if( !$dryrun ) { mkdir "$target_dir/$class_name" or print $! };
    foreach my $slide ( @{ $class_config_info{$class_name} } )
    {
      $dir_name = $$slide{dir_name};
      $num_imgs = $$slide{img_quant};
      if( $DEBUG2 or $verbose or $dryrun )
      {
        print "\t\t Slide \"$dir_name\", Images allocated to class $class_name: $num_imgs\n";
      }
      print "\t\t\tOpening $source_dir/$dir_name:\n" if( $DEBUG2 or $verbose or $dryrun );
      # remember, there could be original tiffs, deconvolved tiffs with H_ or E_ 
      # prefixes or suffixes, and also calculated sigs, all in this directory.
      # This will even include the "." and ".." paths.
      opendir( DIR, "$source_dir/$dir_name" ) or die "Error opening dir $dir_name"; 
      @full_file_list = readdir(DIR);
      for($i = 0; $i <= $#full_file_list; $i++ )
      {
        push @tiff_file_list, $full_file_list[$i] if( $full_file_list[$i] =~ /\.tif$/ ) ;

				
        if( $full_file_list[$i] =~ /(.*)\.sig$/ )
        {
          if( $no_tiffs ){
						push @sig_file_list, $full_file_list[$i];
					}
					else {
						my $base = $1;
						$base =~ s/-l//g;
						#$base =~ s/-S500//g;
						#$base =~ s/-t4//g;
						#$base =~ s/_\d_\d//g;
						$base .= ".tif";
						push @sig_file_list, $base;
					}
        }
      }
      if( $#tiff_file_list == -1 and $#sig_file_list == -1 )
      {
        die "*******************************************\nFatal Error: No .tif or .sig files in directory $source_dir/$dir_name.\n";
      }

      my @working_file_list;
      # if we found any tiffs
      if( $#tiff_file_list != -1 )
      {
        @working_file_list = @tiff_file_list;
        print "\t\t\tFound tiffs in $source_dir/$dir_name\n" if( $DEBUG2 or $verbose or $dryrun );
      }
      else
      {
        @working_file_list = @sig_file_list;
      }
      # if there are more tiffs than are called for in the experiment, we randomize the order
      if( ($#working_file_list +1 ) > ( $num_imgs - 1) )
      {
        # randomize the file list
        for ($i = @working_file_list; --$i; )
        {
          $j = int rand ($i+1);
          # print "i: $i j: $j \n";
          next if $i == $j;
          @working_file_list[$i,$j] = @working_file_list[$j,$i];
        }
      }
      else  # if there are less tiffs than are called for, give a warning
      {
        warn "************************\nWarning: Only " . ($#working_file_list + 1) . " tiffs found in $dir_name, and is less than $num_imgs specified in configuration file.\n";
      }
      my @list_of_sigs;
      # Note that the loop will bail out without error if the number of images
      # asked for exceeds 
      for( $i = 0; $i < $num_imgs && $i <= $#working_file_list; $i++ ) {
        # Unshift the first $num_imgs off the newly randomized list
        $file = $working_file_list[$i];
        if( $DEBUG2 or $verbose or $dryrun ) {
          print "\t\t\t\tImage $i will be $file\n";
        }
        print FOF "$source_dir/$dir_name/$file\t$class_name\n";

#            if( !$dryrun ) {
#             link( "$source_dir/$dir_name/$file", "$target_dir/$class_name/$file" ) or die "Can't create hard link from $source_dir/$dir_name/$file to $target_dir/$class_name/$file: $!\n";
#            }
#
#            if( $DEBUG2 or $verbose or $dryrun ) {
#              print "\t\t\t\tCreating links for $source_dir/$dir_name/${file_stem}*.sig to $target_dir/$class_name\n";
#            }
#            if( !$dryrun ) {
#              @list_of_sigs = split( "\n", `find $source_dir/$dir_name -name \"${file_stem}\"*.sig` );
#              foreach (@list_of_sigs) {
#                /^.*\/(.+)/; # This is idiomatic perl - it means perform that regular expression operation on the default variable $_, produced by foreach (@list_of_args) loop.
#                  link( "$source_dir/$dir_name/$1", "$target_dir/$class_name/$1" ) or die "Can't create hard link from $source_dir/$dir_name/$1 to $target_dir/$class_name/$1: $!\n";
#              }
#            }
#          } # END if $file =~ /.tif/
      } # END for i = 0 to nim_imgs etc

      @full_file_list = ();
      @tiff_file_list = ();
      @sig_file_list = ();
      closedir DIR;
    } # end copying images out of slides
  } # end creating the classes
  close FOF;
	
	if( defined $write_fisher_weights && $write_fisher_weights ne "" ) {
	  $write_fisher_weights = "-vw$target_dir/$experiment_name.fisher_weights";
	} else {
	  $write_fisher_weights = "";
	}

	my $cmd;
	$cmd = "$path_to_wndchrm train -m $test_args $tile_scheme $large_feature_set $fof_path $target_dir/$experiment_name.fit $silence_wndchrm";
	if( $DEBUG2 or $verbose or $dryrun or $silence_wndchrm) {
	  print "\nWndchrm train command: \n$cmd\n";
	}
	system( $cmd ) if( !$dryrun );


        if( defined $start_fraction && defined $finish_fraction && defined $fraction_increment_interval ) {
	  # The name of the dendrogram is dictated by the name of the .fit file submitted to WND-CHRM
	  # so we'll need to change the name of the .fit file each time through the loop
	  my $original_fit_filename = "$experiment_name.fit";
	  my $old_fit_filename = $original_fit_filename;
	  my $new_fit_filename;
	  for( $feature_usage_fraction = $start_fraction; $feature_usage_fraction <= $finish_fraction; $feature_usage_fraction += $fraction_increment_interval ) {
	    if( $DEBUG2 or $verbose or $dryrun or $silence_wndchrm ) {
	      print "\n\tExecuting wndchrn test command for feature fraction $feature_usage_fraction\n";
	    }
	    my $fraction_string;
	    if( $feature_usage_fraction =~ /\d?\.(\d+)/ ) {
	      $fraction_string = $1;
	    } else {
	      die "\n\n*****************************\nINTERNAL ERROR\nSomething wrong with the feature usage fraction interval: $feature_usage_fraction\n";
	    } 
	    $html_file = "$target_dir/${experiment_name}_f$fraction_string.html";
	    $new_fit_filename = "${experiment_name}_f$fraction_string.fit";
	    if( defined $self_set && $self_set ne "" ) {
	      $self_set = "$new_fit_filename";
	    } else {
	      $self_set = "";
	    }
	    if( $DEBUG2 or $verbose or $dryrun or $silence_wndchrm ) {
	      print "\tChanging the name of $old_fit_filename to $new_fit_filename so the dendrogram will be named correctly.\n";
	    }
	    if( !$dryrun ) {
	      move( "$target_dir/$old_fit_filename", "$target_dir/$new_fit_filename" ) 
	        or die "***********************\nError: Can't move $old_fit_filename to $new_fit_filename. $!\n";
	    }
	    # Reset for next time through loop
	    $old_fit_filename = $new_fit_filename;
	    $cmd = "$path_to_wndchrm test $weighted_nearest_neighbor $test_args $large_feature_set $tile_scheme $training_images $test_images -f$feature_usage_fraction $test_repetitions $write_fisher_weights $phylip_command $self_set $target_dir/$new_fit_filename $html_file $silence_wndchrm";
	    if( $DEBUG2 or $verbose or $dryrun or $silence_wndchrm ) {
	      print "\nWndchrn test command: \n$cmd\n";
	    }
	    system( $cmd ) if( !$dryrun );
	    # clean up by compying dend files if any to experiment dir
	    if( -e "$path_to_phylip/dend_file.txt" ) {
	  	  print "\tMoving  $path_to_phylip/dend_file.txt to $target_dir/${experiment_name}_f${feature_usage_fraction}_dend_file.txt\n" if( $DEBUG2 or $verbose or $dryrun or $silence_wndchrm);
	 	  move( "$path_to_phylip/dend_file.txt", "$target_dir/${experiment_name}_f${feature_usage_fraction}_dend_file.txt" ) if !$dryrun;
	    } else {
		  print "*************************\nError: $path_to_phylip/dend_file.txt wasn't created!\n";
	    }
	  } # end iterate over different veature values
	  # Change the name of the fit file back to the original name
	  if( !$dryrun ) {
	    move( "$target_dir/$new_fit_filename", "$target_dir/$original_fit_filename" ) 
	      or die "***********************\nError: Can't move $new_fit_filename to $original_fit_filename. $!\n";
	  }
	} # END code that runs a battery of wndchrm tests where feature fraction is varied
	else
	{ 
	  # BEGIN standard wndchrm test code
	  # target_dir and experiment_name change between iterations.
	  if( defined $html_file && $html_file ne "" ) {
	    $html_file = "$target_dir/$experiment_name.html";
	  } else {
	    $html_file = "";
	  }
	  if( defined $self_set && $self_set ne "" ) {
	    $self_set = "$target_dir/$experiment_name.fit";
	  } else {
	    $self_set = "";
	  }
	  $cmd = "$path_to_wndchrm test $test_args $weighted_nearest_neighbor $tile_scheme $training_images $test_images $feature_usage_fraction $test_repetitions $write_fisher_weights $phylip_command $self_set $target_dir/$experiment_name.fit $html_file $silence_wndchrm";
	  if( $DEBUG2 or $verbose or $dryrun or $silence_wndchrm ) {
	    print "\nWndchrn test command: \n$cmd\n";
	  }
	  if( !$dryrun )
	  {
	    system( $cmd );
	    # clean up by compying dend files if any to experiment dir
	    if( defined $path_to_phylip ) {
				if( -e "$path_to_phylip/dend_file.txt" )
				{
					print "\tMoving  $path_to_phylip/dend_file.txt to $target_dir/${experiment_name}_dend_file.txt\n" if( $DEBUG2 or $verbose or $dryrun or $silence_wndchrm);
					move( "$path_to_phylip/dend_file.txt", "$target_dir/${experiment_name}_dend_file.txt" ) if !$dryrun;
				}
				else
				{
					print "*************************\nError: $path_to_phylip/dend_file.txt wasn't created!\n";
				}
			} #END if defined path_to_phylip
	  } # END if( !$dryrun )
	} # END running standard WND-CHARM test experiment


}
##################################################################################
# This is the code that lets you specify the train test split
##################################################################################
my $DEBUG6 = 0;

sub RunExperimentTrainTestSplitsSpecified {
  if( $DEBUG6 or $verbose or $dryrun ) {
    print "\n\n*******************************\nBegin specified train/test split experiment $experiment_name:\n";
  }
  my $class_name;
  my $dir_name;
  my $num_imgs;
  my @tiff_file_list;
  my $file;
  my $file_stem;
  my( $i, $j );

  $target_dir .= "/" . $experiment_name;
  if( $DEBUG6 or $verbose or $dryrun ) {
    print "\tMaking experiment directory $target_dir\n";
  }
  if( -e $target_dir ) {
    print "\t*******$target_dir exists... skipping...\n\n";
    return;
  }
  if( !$dryrun ) { mkdir "$target_dir" or print $! };

  if( $DEBUG6 or $verbose or $dryrun ) {
    print "\tMaking directory $target_dir/training_set\n";
  }
  if( !$dryrun ) { mkdir "$target_dir/training_set" or print $! };
  
  if( $DEBUG6 or $verbose or $dryrun ) {
    print "\tMaking directory $target_dir/test_set\n";
  }
  if( !$dryrun ) { mkdir "$target_dir/test_set" or print $! };

  ITERATE_OVER_CLASSES: foreach $class_name ( sort keys %class_config_info ) {
    if( $DEBUG6 or $verbose or $dryrun ){
      print "\tClass $class_name:\n";
    }

    # First do the training set
    if( $DEBUG6 or $verbose or $dryrun ) {
      print "\t\tMaking training set directory $target_dir/training_set/$class_name\n";
    }
    if( !$dryrun ) { mkdir "$target_dir/training_set/$class_name" or print $! };
    my @tiff_file_list = @{ $class_config_info{ $class_name }->{training_set} };
    if( $DEBUG6 or $verbose or $dryrun ){
      print "\t\tImages allocated to $class_name training set: $#tiff_file_list\n";
    }

    my @list_of_sigs;
    # Note that the loop will bail out without error if the number of images
    # asked for exceeds 
    for( $i = 0; $i <= $#tiff_file_list; $i++ ) {
      # Unshift the first $num_imgs off the list
      $file = $tiff_file_list[$i]->{name};
      $dir_name = $tiff_file_list[$i]->{dir};
      if( $DEBUG6 or $verbose or $dryrun ) {
        print "\t\t\t\tImage $i will be $file\n";
      }
      if( $file =~ /^(.*)\.tif$/ )
      {
        $file_stem = $1;
        # copy the file and the sigs associated with the file
        # need to be done in two operations, because image42* will copy
        # not only image42.tif and image42_1.sig, but also unexpectedly
        # image420, image421, etc.
        if( $DEBUG6 or $verbose or $dryrun ) {
          print "\t\t\t\tCreating link from $source_dir/$dir_name/$file to $target_dir/training_set/$class_name/$file\n";
        }
        if( !$dryrun ) {
          link( "$source_dir/$dir_name/$file", "$target_dir/training_set/$class_name/$file" ) or die "Can't create hard link from $source_dir/$dir_name/$file to $target_dir/training_set/$class_name/$file: $!\n";
        }

        if( $DEBUG6 or $verbose or $dryrun ) {
          print "\t\t\t\tCreating links for $source_dir/$dir_name/${file_stem}*.sig to $target_dir/training_set/$class_name\n";
        }
        if( !$dryrun ) {
          @list_of_sigs = split( "\n", `find $source_dir/$dir_name -name ${file_stem}*.sig` );
          foreach (@list_of_sigs) {
            /^\S*\/(\S+)/; # This is idiomatic perl - it means perform that regular expression operation on the default variable $_, produced by foreach (@list_of_args) loop.
            link( "$source_dir/$dir_name/$1", "$target_dir/training_set/$class_name/$1" ) or die "Can't create hard link from $source_dir/$dir_name/$1 to $target_dir/training_set/$class_name/$1: $!\n";
          }
        }
      } # END if $file =~ /.tif/
    }
    @tiff_file_list = ();
    # Next do the test set
    if( $DEBUG6 or $verbose or $dryrun ) {
      print "\t\tMaking test set directory $target_dir/test_set/$class_name\n";
    }
    if( !$dryrun ) { mkdir "$target_dir/test_set/$class_name" or print $! };
    @tiff_file_list = @{ $class_config_info{ $class_name }->{test_set} };
    if( $DEBUG6 or $verbose or $dryrun ){
      print "\t\tImages allocated to $class_name test set: $#tiff_file_list\n";
    }

    # my @list_of_sigs;
    # Note that the loop will bail out without error if the number of images
    # asked for exceeds 
    for( $i = 0; $i <= $#tiff_file_list; $i++ ) {
      # Unshift the first $num_imgs off the list
      $file = $tiff_file_list[$i]->{name};
      $dir_name = $tiff_file_list[$i]->{dir};
      if( $DEBUG6 or $verbose or $dryrun ) {
        print "\t\t\t\tImage $i will be $file\n";
      }
      if( $file =~ /^(.*)\.tif$/ ) {
        $file_stem = $1;
        # copy the file and the sigs associated with the file
        # need to be done in two operations, because image42* will copy
        # not only image42.tif and image42_1.sig, but also unexpectedly
        # image420, image421, etc.
        if( $DEBUG6 or $verbose or $dryrun ) {
          print "\t\t\t\tCreating link from $source_dir/$dir_name/$file to $target_dir/test_set/$class_name/$file\n";
        }
        if( !$dryrun ) {
          link( "$source_dir/$dir_name/$file", "$target_dir/test_set/$class_name/$file" ) or die "Can't create hard link from $source_dir/$dir_name/$file to $target_dir/test_set/$class_name/$file: $!\n";
        }

        if( $DEBUG6 or $verbose or $dryrun ) {
          print "\t\t\t\tCreating links for $source_dir/$dir_name/${file_stem}*.sig to $target_dir/test_set/$class_name\n";
        }
        if( !$dryrun ) {
          @list_of_sigs = split( "\n", `find $source_dir/$dir_name -name ${file_stem}*.sig` );
          foreach (@list_of_sigs) {
            /^\S*\/(\S+)/; # This is idiomatic perl - it means perform that regular expression operation on the default variable $_, produced by foreach (@list_of_args) loop.
            link( "$source_dir/$dir_name/$1", "$target_dir/test_set/$class_name/$1" ) or die "Can't create hard link from $source_dir/$dir_name/$1 to $target_dir/test_set/$class_name/$1: $!\n";
          }
        }
      } # END if $file =~ /.tif/
    }
    @tiff_file_list = ();
  } # end creating the classes
	
	if( defined $write_fisher_weights && $write_fisher_weights ne "" ) {
	  $write_fisher_weights = "-vw$target_dir/$experiment_name.fisher_weights";
	} else {
	  $write_fisher_weights = "";
	}

	my $cmd;
	$cmd = "$path_to_wndchrm train -m $tile_scheme $large_feature_set $target_dir/training_set $target_dir/training_set/${experiment_name}_training_set.fit $silence_wndchrm";
	if( $DEBUG6 or $verbose or $dryrun or $silence_wndchrm) {
	  print "\nWndchrm train training set command: \n$cmd\n";
	}
	system( $cmd ) if( !$dryrun );
	$cmd = "$path_to_wndchrm train -m $tile_scheme $large_feature_set $target_dir/test_set $target_dir/test_set/${experiment_name}_test_set.fit $silence_wndchrm";
	if( $DEBUG6 or $verbose or $dryrun or $silence_wndchrm) {
	  print "\nWndchrm train test set command: \n$cmd\n";
	}
	system( $cmd ) if( !$dryrun );


  if( defined $start_fraction && defined $finish_fraction && defined $fraction_increment_interval ) {
	  # The name of the dendrogram is dictated by the name of the .fit file submitted to WND-CHRM
	  # so we'll need to change the name of the .fit file each time through the loop
	  my $original_fit_filename = "$experiment_name.fit";
	  my $old_fit_filename = $original_fit_filename;
	  my $new_fit_filename;
	  for( $feature_usage_fraction = $start_fraction; $feature_usage_fraction <= $finish_fraction; $feature_usage_fraction += $fraction_increment_interval ) {
	    if( $DEBUG6 or $verbose or $dryrun or $silence_wndchrm ) {
	      print "\n\tExecuting wndchrn test command for feature fraction $feature_usage_fraction\n";
	    }
	    my $fraction_string;
	    if( $feature_usage_fraction =~ /\d?\.(\d+)/ ) {
	      $fraction_string = $1;
	    } else {
	      die "\n\n*****************************\nINTERNAL ERROR\nSomething wrong with the feature usage fraction interval: $feature_usage_fraction\n";
	    } 
	    $html_file = "$target_dir/${experiment_name}_f$fraction_string.html";
	    $new_fit_filename = "${experiment_name}_f$fraction_string.fit";
	    if( $DEBUG6 or $verbose or $dryrun or $silence_wndchrm ) {
	      print "\tChanging the name of $old_fit_filename to $new_fit_filename so the dendrogram will be named correctly.\n";
	    }
	    if( !$dryrun ) {
	      move( "$target_dir/training_set/$old_fit_filename", "$target_dir/training_set/$new_fit_filename" ) 
	        or die "***********************\nError: Can't move $old_fit_filename to $new_fit_filename. $!\n";
	    }
	    # Reset for next time through loop
	    $old_fit_filename = $new_fit_filename;
      my $training_set_fit_name = "$target_dir/training_set/$experiment_name.fit";
      my $test_set_fit_name = "$target_dir/test_set/$experiment_name.fit";;

	    $cmd = "$path_to_wndchrm test $weighted_nearest_neighbor $test_args $tile_scheme $training_images $test_images -f$feature_usage_fraction $test_repetitions $write_fisher_weights $phylip_command $training_set_fit_name $test_set_fit_name $html_file $silence_wndchrm";
	    if( $DEBUG6 or $verbose or $dryrun or $silence_wndchrm ) {
	      print "\nWndchrn test command: \n$cmd\n";
	    }
	    system( $cmd ) if( !$dryrun );
	    # clean up by compying dend files if any to experiment dir
	    if( -e "$path_to_phylip/dend_file.txt" ) {
	  	  print "\tMoving  $path_to_phylip/dend_file.txt to $target_dir/${experiment_name}_f${feature_usage_fraction}_dend_file.txt\n" if( $DEBUG6 or $verbose or $dryrun or $silence_wndchrm);
	 	  move( "$path_to_phylip/dend_file.txt", "$target_dir/${experiment_name}_f${feature_usage_fraction}_dend_file.txt" ) if !$dryrun;
	    } else {
		  print "*************************\nError: $path_to_phylip/dend_file.txt wasn't created!\n";
	    }
	  } # end iterate over different veature values
	  # Change the name of the fit file back to the original name
	  if( !$dryrun ) {
	    move( "$target_dir/$new_fit_filename", "$target_dir/$original_fit_filename" ) 
	      or die "***********************\nError: Can't move $new_fit_filename to $original_fit_filename. $!\n";
	  }
	} # END code that runs a battery of wndchrm tests where feature fraction is varied
	else
	{ 
	  # BEGIN standard wndchrm test code
	  # target_dir and experiment_name change between iterations.
	  if( defined $html_file && $html_file ne "" ) {
	    $html_file = "$target_dir/$experiment_name.html";
	  } else {
	    $html_file = "";
	  }
    my $training_set_fit_name = "$target_dir/training_set/${experiment_name}_training_set.fit";
    my $test_set_fit_name = "$target_dir/test_set/${experiment_name}_test_set.fit";;

	  $cmd = "$path_to_wndchrm test $test_args $weighted_nearest_neighbor $tile_scheme $training_images $test_images $feature_usage_fraction $test_repetitions $write_fisher_weights $phylip_command $training_set_fit_name $test_set_fit_name $html_file $silence_wndchrm";
	  if( $DEBUG6 or $verbose or $dryrun or $silence_wndchrm ) {
	    print "\nWndchrn test command: \n$cmd\n";
	  }
	  if( !$dryrun )
	  {
	    system( $cmd );
	    # clean up by compying dend files if any to experiment dir
	    if( -e "$path_to_phylip/dend_file.txt" ) {
	  	print "\tMoving  $path_to_phylip/dend_file.txt to $target_dir/${experiment_name}_dend_file.txt\n" if( $DEBUG6 or $verbose or $dryrun or $silence_wndchrm);
	  	move( "$path_to_phylip/dend_file.txt", "$target_dir/${experiment_name}_dend_file.txt" ) if !$dryrun;
	    } else {
	  	print "*************************\nError: $path_to_phylip/dend_file.txt wasn't created!\n";
	    }
	  } # END if( !$dryrun )
	} # END running standard WND-CHARM test experiment


}


# sub SubtractIntraclassWeights
# for each class
#   make a subdirectory in the target directory
#   make a subsubdirectory for each slide
#   copy n number of images and sigs into each subsubdirectory
#   fire off wndchrm train -m and get a sub.fit file
#   fireoff wndchrm test -vwWEIGHTFILE
#   check that the weightfile was created, if so pack the path to an array
# for eack weightfile
#   open it
#   
1;

__END__

# This is a wndchrm experiment file!
# Lines that begin with a # are treated as a comment

# Experiment name - must be all one word because this becomes the name of a directory
experiment my_experiment

# Full path to your wndchrm executable
path_to_wndchrm	/home/colettace/wndchrm_home_directory/wndchrm

# Source directory is the folder where your tiffs and/or sigs live
source_dir /home/colettace/where/the/images/and/sigs/live

# Target directory is where you want to create your new experiment class dir structure
# tiffs and sigs will be copied into here
target_dir /home/colettace/wndchrm/products/go/in/here

# Experiment class definition section

# For a standard WND-CHARM experiment, this is how you define the image classes:

# column 1: the word "class"
# column 2: path of class 1's images and sigs relative to source dir specified above
# column 3: the name of the class that these images belong to
# column 4: the number of images from the source directory that should be copied into the class folder

class	sourcedir1				classA	90
class	sourcedir2				classB	90
class	sourcedir3				classC	90
class	sourcedir4				classA	90
class	sourcedir5				classB	90
class	sourcedir6				classC	90
class an_organizing_folder/sourcedir7		classD	60
class an_organizing_folder/sourcedir8		classD	60
class	an_organizing_folder/sourcedir9		classD	60
class an_organizing_folder/sourcedir10	classD	60

#########################################################
# SYNTAX FOR SPECIFYING A PAIRWISE EXPERIMENT
#########################################################

# For a pairwise comparison WND-CHARM experiment,
#   use the above class definition format, but also include the word group
#   followed by a group identifier. Example:

class	sourcedir1				classA	90	group	1
class	sourcedir2				classB	90	group	2
class	sourcedir3				classC	90	group	3
class	sourcedir4				classA	90	group	1
class	sourcedir5				classB	90	group	2
class	sourcedir6				classC	90	group	3
class an_organizing_folder/sourcedir7		classD	60	group	4
class an_organizing_folder/sourcedir8		classD	60	group	4
class	an_organizing_folder/sourcedir9		classD	60	group	4
class an_organizing_folder/sourcedir10	classD	60	group	4

# Note how the group numbers (column 6) correspond with the assigned class name (col 3).
# In addition to 1 vs. 1 pairwise experiments, you can put multiple classes in a group,
# to do pairwise experiments on the groups instead of individual classes.
# This enables the user to perform 2 vs. 2, 3 vs. 3, experiments, and so on.


#########################################################
# SPECIFYING INDIVIDUAL IMAGES FOR THE TRAINING AND TEST SETS
#########################################################

# If you need to specify which individual images should go into the training set and test set,
# list each individual image on its own line using the following syntax:

# For a training image:
# training_image  Image123.tif  dir the_containing_directory  class the_class_name

# So it's the word "training_image", followed by the name of the image, followed by
# the word "dir", followed by the directory that contains the image, followed by
# the word class, followed by the name of the class that the image belongs to in this
# experiment

# For a test image:
# test_image  Image123.tif  dir the_containing_directory  class the_class_name

# in other words, its the same as specifying a training image, just substituting
# the word "training_image" for "test_image"

# Here are some more examples of how to specify individual images for training and testing
test_image	Image964.tif	dir	009	class	009
test_image	Image971.tif	dir	009	class	009
test_image	Image972.tif	dir	009	class	009
test_image	Image978.tif	dir	009	class	009
test_image	Image981.tif	dir	009	class	009
test_image	Image983.tif	dir	009	class	009
test_image	Image993.tif	dir	009	class	009
training_image	Image001.tif	dir	000	class	000
training_image	Image003.tif	dir	000	class	000
training_image	Image004.tif	dir	000	class	000
training_image	Image005.tif	dir	000	class	000
training_image	Image007.tif	dir	000	class	000
training_image	Image008.tif	dir	000	class	000

#########################################################
# AVAILABLE EXPERIMENT PARAMETERS
#########################################################

# The following is an explanation of the available experiment parameters

# Section 1a: WND-CHARM classifier TRAINING parameters that have wndchrm command line equivalents

# tile_scheme	<n>
#    Equivalent to the "-t" wndchrm command line argument
#    Tells WND-CHARM to break training and test images up into a matrix of n x n subimages 
tile_scheme	2

# large_feature_set
#    Equivalent to the "-l" wndchrm command line argument
#    Use the large feature set when calculating sigs
large_feature_set


# Section 1b: WND-CHARM classifier TESTING parameters that have wndchrm command line equivalents

# training_images <n>
#    Equivalent of the "-i" command line argument
#    Specify the number of images per class to form the classifier training set
training_images	89

# test_images	<n>
#    Equivalent to the "-j" command line argument
#    Specify the number of images per class that should form the test set
test_images	1

# test_repetitions	<n>
#    Equivalent to the "-n" command line argument
#    Specify the number of training set/test set splits when testing the classifier
test_repetitions	30

# feature_usage_fraction	<f>
#    Equivalent to the "-f" command line argument
#    A number on the interval from ( 0.0, 1.0 ] that specifies the
#    how many of the top ranking image features to use when classifying
#    the images in the test set.
feature_usage_fraction	1.0

# path_to_phylip	/unix/full/path/to/phylip/base
#    Equivalent of the "-p" command line argument
#    Create a dendrogram of the test data using the class similarity values
#    Specify the path to the base directory where the phylip program is
#    All intermediate files associated with creating the dendrogram will be
#    copied to the experiment output directory
path_to_phylip			/home/colettace/src/phylip-3.69

# html_file			
#    Equivalent of specifying a path and file name for the html report
#    Note that you must explicitly ask for an html result file,
#    else one will not be generated. Output goes to experiment output directory
html_file

# write_fisher_weights
#    Equivalent to the -w/path/to/fisher/weights/file on the command line
#    Write out the fisher weights to a file, whose name is determined programmatically
#    File is written to the experiment output directory.
write_fisher_weights

# weighted_nearest_neighbor
#    Equivalent to the "-w" command line argument
#    Use the alternative Weighted Nearest Neighbor algorithm to calculate distances from test image
#    to training set images, instead of default WND (Weighted Neighbor Distance)

# self_set
#    Use the entire training set as the test set
#    Equivalent of specifying the training set twice on the command line
self_set

# no_tiffs
#    Use when there are only .sig files.
no_tiffs

# additional_test_args <stuff>
#    Any native wndchrm functionality not implemented above can be passed through
#    to the command line using this parameter
additional_test_args -v-/path/to/artifact/weights 


# Section 2: WND-CHARM classifier TESTING functionality NOT INCLUDED in the wndchrm binary

# vary_feature_fraction	start	<a>	finish	<b>	increment	<c>
#    Perform WND-CHARM test experiments where the feature usage fraction is a
#    parameter that is varied
#    example:
vary_feature_fraction	start	0.1	finish	1.0	increment 0.1


# experiment_repetitions	<n>
#    Repeat the entire experiment this many times, randomly generating entirely new training sets
#    (and test sets) from the source image pool. Differs entirely from the -n# wndchrm switch.
#    This is useful when you have a large pool of images from which to create new
#    training sets, especially when the number of images in each source pool is different.
#    This is the equivalent of running wndchrm train n times, with each time randomly
#    selecting a new set of images to include in the classifier (.fit) file.
#    The target directory will contain a subdirectory for each experiment run
experiment_repetitions	30

# silence_wndchrm
#    Run_wndchrm in silent mode
#    This directs all output to the bit bucket ( >/dev/null)
silence_wndchrm
